//
// Copyright (c) 2024 BPer
//
#include <bpersdp/BPerRtcSdp.h>

#include <bperutil/sys/BPerLog.h>
#include <bperutil/sys/BPerSsrc.h>
#include <bperutil/sys/BPerCString.h>

#include <bperssl/BPerSsl.h>
#include <bpersdp/BPerMediaDesc.h>
#include <bpersdp/BPerMediaPayloadType.h>

#define  kCRLF  (char*)"\r\n"

void bPer_create_rtcsdp(BPerSdp *sdp) {
    if (sdp == NULL) return;
    sdp->in_media_session = BPerFalse;
    bPer_itoa(0, sdp->version, 10);
    sdp->start_time = 0;
    sdp->end_time = 0;
}

void bPer_destroy_rtcsdp(BPerSdp *sdp) {
    if (sdp == NULL) return;
    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_destroy_mediadesc(&sdp->media_descs.payload[i]);
    }
    bPer_destroy_BPerMediaDescVector(&sdp->media_descs);
}

char *bPer_rtcsdp_getnull(char *os) {
    char *p = os;
    while (1) {
        if (*p == 0)
            return p;
        p++;
    }
}

BPerMediaDesc *bPer_rtcsdp_find_media_descs(BPerSdp *sdp, char *type) {


    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        if (bPer_strcmp(sdp->media_descs.payload[i].type, type) == 0)
            return &sdp->media_descs.payload[i];
    }
    return NULL;
}

void bPer_rtcsdp_set_ice_ufrag(BPerSdp *sdp, char *ufrag) {

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_strcpy(sdp->media_descs.payload[i].session_info.ice_ufrag, ufrag);
    }
}

void bPer_rtcsdp_set_ice_pwd(BPerSdp *sdp, char *pwd) {

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_strcpy(sdp->media_descs.payload[i].session_info.ice_pwd, pwd);
    }
}

void bPer_rtcsdp_set_dtls_role(BPerSdp *sdp, char *dtls_role) {

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_strcpy(sdp->media_descs.payload[i].session_info.setup, dtls_role);
    }
}

void bPer_rtcsdp_set_fingerprint_algo(BPerSdp *sdp, char *algo) {

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_strcpy(sdp->media_descs.payload[i].session_info.fingerprint_algo, algo);
    }

}

void bPer_rtcsdp_set_fingerprint(BPerSdp *sdp, char *fingerprint) {

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_strcpy(sdp->media_descs.payload[i].session_info.fingerprint,
                    fingerprint);
    }

}

void bPer_rtcsdp_add_candidate(BPerSdp *sdp, char *ip, int port, char *type) {
    // @see: https://tools.ietf.org/id/draft-ietf-mmusic-ice-sip-sdp-14.html#rfc.section.5.1
    BPerCandidate candidate;

    bPer_strcpy(candidate.ip, ip);
    candidate.port = port;
    bPer_strcpy(candidate.type, type);


    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        bPer_insert_BPerCandidateVector(&sdp->media_descs.payload[i].candidates,
                                        &candidate);
    }
}

char *bPer_rtcsdp_get_ice_ufrag(BPerSdp *sdp) {
    // Becaues we use BUNDLE, so we can choose the first element.

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        return sdp->media_descs.payload[i].session_info.ice_ufrag;
    }
    return "";
}

char *bPer_rtcsdp_get_ice_pwd(BPerSdp *sdp) {
    // Becaues we use BUNDLE, so we can choose the first element.

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        return sdp->media_descs.payload[i].session_info.ice_pwd;
    }
    return "";
}

char *bPer_rtcsdp_get_dtls_role(BPerSdp *sdp) {
    // Becaues we use BUNDLE, so we can choose the first element.

    for (int i = 0; i < sdp->media_descs.vsize; i++) {
        return sdp->media_descs.payload[i].session_info.setup;
    }
    return "";
}

int32_t bPer_rtcsdp_parse_origin(BPerSdp *sdp, char *content) {
    int32_t err = BPer_Ok;
    BPerStrings str;
    bPer_cstr_split(content, " ", &str);
    if (str.vsize < 5)
        return 1;
    // @see: https://tools.ietf.org/html/rfc4566#section-5.2
    // o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
    // eg. o=- 9164462281920464688 2 IN IP4 127.0.0.1

    bPer_strcpy(sdp->username, str.str[0]);
    bPer_strcpy(sdp->session_id, str.str[1]);
    bPer_strcpy(sdp->session_version, str.str[2]);
    bPer_strcpy(sdp->nettype, str.str[3]);
    bPer_strcpy(sdp->addrtype, str.str[4]);
    bPer_strcpy(sdp->unicast_address, str.str[5]);

    bPer_destroy_strings(&str);
    return err;
}

int32_t bPer_rtcsdp_parse_version(BPerSdp *sdp, char *content) {
    int32_t err = BPer_Ok;
    // @see: https://tools.ietf.org/html/rfc4566#section-5.1
    if (bPer_strlen(content))
        bPer_strcpy(sdp->version, content);
    else
        bPer_itoa(0, sdp->version, 10);

    return err;
}

int32_t bPer_rtcsdp_parse_session_name(BPerSdp *sdp, char *content) {
    int32_t err = BPer_Ok;
    // @see: https://tools.ietf.org/html/rfc4566#section-5.3
    // s=<session name>
    bPer_strcpy(sdp->session_name, content);

    return err;
}

int32_t bPer_rtcsdp_parse_timing(BPerSdp *sdp, char *content) {
    int32_t err = BPer_Ok;

    // @see: https://tools.ietf.org/html/rfc4566#section-5.9
    // t=<start-time> <stop-time>
    BPerStrings str;
    bPer_cstr_split(content, " ", &str);

    if (str.vsize < 2)
        return 1;

    sdp->start_time = atol(str.str[0]);
    sdp->end_time = atol(str.str[1]);

    bPer_destroy_strings(&str);
    return err;
}

int32_t bPer_rtcsdp_parse_attr_group(BPerSdp *sdp, char *value) {
    int32_t err = BPer_Ok;
    // @see: https://tools.ietf.org/html/rfc5888#section-5
    BPerStrings str;
    bPer_cstr_split(value, " ", &str);
    if (str.vsize == 0)
        return 1;

    bPer_strcpy(sdp->group_policy, str.str[0]);
    for (int i = 1; i < str.vsize; i++) {
        bPer_insert_stringVector(&sdp->groups, str.str[i]);
    }

    bPer_destroy_strings(&str);
    return err;
}

int32_t bPer_rtcsdp_parse_media_description(BPerSdp *sdp, char *content) {
    int32_t err = BPer_Ok;

    // @see: https://tools.ietf.org/html/rfc4566#section-5.14
    // m=<media> <port> <proto> <fmt> ...
    // m=<media> <port>/<number of ports> <proto> <fmt> ...

    BPerStrings str;
    bPer_cstr_split(content, " ", &str);
    if (str.vsize < 4)
        return bPer_error_wrap(1,
                               "rtcsdp_parse_media_description error content=%s", content);

    BPerMediaDesc md;
    bPer_memset(&md, 0, sizeof(BPerMediaDesc));
    bPer_insert_BPerMediaDescVector(&sdp->media_descs, &md);
    BPerMediaDesc *desc = &sdp->media_descs.payload[sdp->media_descs.vsize - 1];
    bPer_strcpy(desc->type, str.str[0]);
    bPer_strcpy(desc->protos, str.str[2]);
    desc->port = atoi(str.str[1]);
    for (int32_t i = 3; i < str.vsize; i++) {
        BPerMediaPayloadType pt;
        bPer_memset(&pt, 0, sizeof(BPerMediaPayloadType));
        pt.payload_type = atoi(str.str[i]);
        bPer_insert_BPerMediaPayloadTypeVector(&desc->payload_types, &pt);
        bPer_create_stringVector(&desc->payload_types.payload[desc->payload_types.vsize - 1].rtcp_fb);
    }

    if (!sdp->in_media_session) {
        sdp->in_media_session = BPerTrue;
    }
    bPer_destroy_strings(&str);
    return err;
}

int32_t bPer_rtcsdp_parse_attribute(BPerSdp *sdp, char *content) {
    int32_t err = BPer_Ok;
    // @see: https://tools.ietf.org/html/rfc4566#section-5.13
    // a=<attribute>
    // a=<attribute>:<value>
    char *p = bPer_strstr(content, ":");

    char attribute[256];
    char value[256];
    bPer_memset(attribute, 0, sizeof(attribute));
    bPer_memset(value, 0, sizeof(value));
    if (p) {
        bPer_memcpy(attribute, content, p - content);
        bPer_strcpy(value, p + 1);

    }
    if (bPer_strcmp(attribute, "group") == 0) {
        return bPer_rtcsdp_parse_attr_group(sdp, value);
    } else if (bPer_strcmp(attribute, "msid-semantic") == 0) {
        BPerStrings str;
        bPer_cstr_split(value, " ", &str);
        bPer_strcpy(sdp->msid_semantic, str.str[0]);
        for (int j = 1; j < str.vsize; j++) {
            bPer_insert_stringVector(&sdp->msids, str.str[j]);
        }
        bPer_destroy_strings(&str);
    } else {
        return bPer_sessioninfo_parse_attribute(&sdp->session_info, attribute,
                                                value);
    }

    return err;
}

int32_t bPer_rtcsdp_is_unified(BPerSdp *sdp) {

    return sdp->media_descs.vsize > 2 ? 1 : 0;
}

int32_t bPer_rtcsdp_update_msid(BPerSdp *sdp, char *id) {
    int32_t err = BPer_Ok;
    bPer_clear_stringVector(&sdp->msids);
    bPer_insert_stringVector(&sdp->msids, id);


    for (int i = 0; i < sdp->media_descs.vsize; i++) {

        BPerMediaDesc *desc = &sdp->media_descs.payload[i];

        if ((err = bPer_mediadesc_update_msid(desc, id)) != BPer_Ok) {
            bPer_trace("\ndesc %s update msid %s", desc->mid, id);
            return err;
        }
    }

    return err;
}

int32_t bPer_rtcsdp_parse_line(BPerSdp *sdp, char *line) {
    int32_t err = BPer_Ok;

    char *content = line + 2;

    switch (line[0]) {
        case 'o': {
            return bPer_rtcsdp_parse_origin(sdp, content);
        }
        case 'v': {
            return bPer_rtcsdp_parse_version(sdp, content);
        }
        case 's': {
            return bPer_rtcsdp_parse_session_name(sdp, content);
        }
        case 't': {
            return bPer_rtcsdp_parse_timing(sdp, content);
        }
        case 'a': {
            if (sdp->in_media_session) {
                return bPer_mediadesc_parse_line(
                        &sdp->media_descs.payload[sdp->media_descs.vsize - 1], line);
            }
            return bPer_rtcsdp_parse_attribute(sdp, content);
        }
        case 'm': {
            return bPer_rtcsdp_parse_media_description(sdp, content);
        }
        case 'c': {

            break;
        }
        default: {
            bPer_trace("ignore sdp line=%s", line);
            break;
        }
    }

    return err;
}

int32_t bPer_rtcsdp_encode(BPerSdp *sdp, BPerBuffer *os) {
    int32_t err = BPer_Ok;
    char *p = NULL;
    char tmp[2048];
    bPer_memset(tmp, 0, sizeof(tmp));
    bPer_sprintf(tmp, sizeof(tmp), "v=%s%s"
                                   "o=%s %s %s %s %s %s%s"
                                   "s=%s%s"
                                   "t=%"
    PRId64
    " %"
    PRId64
    "%s"
    "a=ice-lite%s", sdp->version, kCRLF, sdp->username, sdp->session_id,
            sdp->session_version, sdp->nettype, sdp->addrtype,
            sdp->unicast_address, kCRLF, sdp->session_name, kCRLF,
            sdp->start_time, sdp->end_time, kCRLF,
            kCRLF);


    if (!sdp->groups.vsize) {
        p = bPer_rtcsdp_getnull(tmp);
        bPer_sprintf(p, sizeof(p), "a=group:%s", sdp->group_policy);
        for (int i = 0; i < sdp->groups.vsize; i++) {
            p = bPer_rtcsdp_getnull(tmp);
            bPer_sprintf(p, sizeof(p), " %s", sdp->groups.payload[i]);
        }
        p = bPer_rtcsdp_getnull(tmp);
        bPer_sprintf(p, sizeof(p), "%s", kCRLF);

    }
    p = bPer_rtcsdp_getnull(tmp);
    bPer_sprintf(p, sizeof(p), "a=msid-semantic: %s", sdp->msid_semantic);
    for (int i = 0; i < sdp->msids.vsize; i++) {
        p = bPer_rtcsdp_getnull(tmp);
        bPer_sprintf(p, sizeof(p), " %s", sdp->msids.payload[i]);
    }
    p = bPer_rtcsdp_getnull(tmp);
    bPer_sprintf(p, sizeof(p), "%s", kCRLF);

    bPer_write_cstring(os, tmp);
    if ((err = bPer_encode_sessionInfo(&sdp->session_info, os)) != BPer_Ok) {
        return printf("encode session info failed");
    }


    for (int i = 0; i < sdp->media_descs.vsize; i++) {

        if ((err = bPer_encode_mediadesc(&sdp->media_descs.payload[i], os))
            != BPer_Ok) {
            return printf("encode media description failed");
        }
    }

    return err;
}

int32_t bPer_rtcsdp_parse(BPerSdp *sdp, char *sdp_str) {
    int32_t err = BPer_Ok;
    BPerStrings strs;

    bPer_cstr_split(sdp_str, "\n", &strs);
    //printf("\nsdp===%s\n",sdp_str.c_str());
    // All webrtc SrsSdp annotated example
    // @see: https://tools.ietf.org/html/draft-ietf-rtcweb-SrsSdp-11
    // Sdp example
    // session info
    // v=
    // o=
    // s=
    // t=
    // media description
    // m=
    // a=
    // ...
    // media description
    // m=
    // a=
    // ...
    // std::istringstream is(sdp_str);
    // std::string line;
    int32_t i = 0;
    for (i = 0; i < strs.vsize; i++) {
        char *line = strs.str[i];
        //bPer_trace("%s", line.c_str());
        if (bPer_strlen(line) < 2 || line[1] != '=') {
            return bPer_error_wrap(ERROR_RTC_SDP_DECODE, "invalid sdp line=%s",
                                   line);
        }

        if ((err = bPer_rtcsdp_parse_line(sdp, line)) != BPer_Ok) {
            return bPer_error_wrap(1, "parse sdp line failed:%s", line);
        }
    }

    // The msid/tracker/mslabel is optional for SSRC, so we copy it when it's empty.

    for (i = 0; i < sdp->media_descs.vsize; i++) {
        BPerMediaDesc *media_desc = &sdp->media_descs.payload[i];
        for (size_t j = 0; j < media_desc->ssrc_infos.vsize; ++j) {

            BPerSSRCInfo *ssrc_info = &media_desc->ssrc_infos.payload[j];
            if (bPer_strlen(ssrc_info->msid) == 0) {
                bPer_strcpy(ssrc_info->msid, media_desc->msid);
            }

            if (bPer_strlen(ssrc_info->msid_tracker) == 0) {
                bPer_strcpy(ssrc_info->msid_tracker, media_desc->msid_tracker);
            }

            if (bPer_strlen(ssrc_info->mslabel) == 0) {
                bPer_strcpy(ssrc_info->mslabel, media_desc->msid);
            }

            if (bPer_strlen(ssrc_info->label) == 0) {
                bPer_strcpy(ssrc_info->label, media_desc->msid_tracker);
            }
        }
    }
    bPer_destroy_strings(&strs);
    return err;
}

